/****************************************************************************
 * Copyright (C) 2009-2010 SciTouch LLC
 * 
 * This file is part of Indigo toolkit.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 3 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ***************************************************************************/

#include "molecule/molecule_arom_match.h"
#include "graph/embedding_enumerator.h"
#include "base_cpp/array.h"

int labels_by_freq[] = {ELEM_C, ELEM_H, ELEM_O, ELEM_N, ELEM_P, ELEM_F,
                            ELEM_S, ELEM_Si, ELEM_Cl, ELEM_Br, ELEM_I, ELEM_At};

static int _compare_degree_asc (const Molecule &mol, int i1, int i2)
{
   return mol.getVertex(i2).degree() - mol.getVertex(i1).degree();
}                     

static int _compare_frequency_base (const Molecule &mol, int i1, int i2)
{
   int label1 = mol.getAtom(i1).label;
   int label2 = mol.getAtom(i2).label;

   if (label1 == 0 && label2 != 0)
      return 1;
   if (label1 != 0 && label2 != 1)
      return -1;

   int is_hetero1 = (label1 != 0 && label1 != ELEM_C && label1 != ELEM_H);
   int is_hetero2 = (label2 != 0 && label2 != ELEM_C && label2 != ELEM_H);
   return is_hetero2 - is_hetero1;
}

static int _compare_frequency_asc (const Molecule &mol, int i1, int i2)
{
   int label1 = mol.getAtom(i1).label;
   int label2 = mol.getAtom(i2).label;
   int idx1, idx2;

   for (idx1 = 0; idx1 < (int)NELEM(labels_by_freq); idx1++)
      if (label1 == labels_by_freq[idx1])
         break;
   for (idx2 = 0; idx2 < (int)NELEM(labels_by_freq); idx2++)
      if (label2 == labels_by_freq[idx2])
         break;

   return idx2 - idx1;
}

static int _compare_in_loop (const Molecule &mol, int i1, int i2)
{
   const Vertex &v1 = mol.getVertex(i1);
   const Vertex &v2 = mol.getVertex(i2);

   int in_loop1 = 0;
   for (int nei = v1.neiBegin(); nei != v1.neiEnd(); nei = v1.neiNext(nei))
   {
      int nei_edge = v1.neiEdge(nei);
      if (mol.bondInRing(nei_edge))
      {
         in_loop1 = 1;
         break;
      }
   }
   int in_loop2 = 0;
   for (int nei = v2.neiBegin(); nei != v2.neiEnd(); nei = v2.neiNext(nei))
   {
      int nei_edge = v2.neiEdge(nei);
      if (mol.bondInRing(nei_edge))
      {
         in_loop2 = 1;
         break;
      }
   }
   return in_loop2 - in_loop1;
}


static int _compare (const int &i1, const int &i2, const void *context)
{
   const Molecule &mol = *(const Molecule *)context;

   bool is_pseudo1 = mol.isPseudoAtom(i1);
   bool is_pseudo2 = mol.isPseudoAtom(i2);
   if (is_pseudo1 && !is_pseudo2)
      return -1;
   if (!is_pseudo1 && is_pseudo2)
      return 1;
   if (is_pseudo1)
      return 0; // All pseudoatoms are the same for transposition for substructure 

   int res;

   res = _compare_frequency_base(mol, i1, i2);
   if (res != 0)
      return res;

   res = _compare_in_loop(mol, i1, i2);
   if (res != 0)
      return res;

   res = _compare_frequency_asc(mol, i1, i2);
   if (res != 0)
      return res;

   return _compare_degree_asc(mol, i1, i2);
}

void Molecule::makeTranspositionForSubstructure (Array<int> &transposition_out) const
{
   int i;

   transposition_out.clear();

   for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i))
      transposition_out.push(i);

   transposition_out.qsort(_compare, this);
}

